function Binary(initData, p, l, bl) {
    var data = initData && initData.constructor == Array ? initData.slice() : [],
    p = p | 0,
    l = l | 0,
    bl = Math.max((bl || 8) | 0, 1),
    mask = m(bl),
    _m = 0xFFFFFFFF; //数据，指针，长度，位长度，遮罩
    this.data = function(index, value) {
        if (!isNaN(value)) data[index] = (value | 0) || 0;
        if (!isNaN(index)) return data[index];
        else return data.slice();
    }
 
    this.read = function() {
        var re;
        if (p >= l) return 0;
        if (32 - (p % 32) < bl) {
            re = (((data[p >> 5] & m(32 - (p % 32))) << ((p + bl) % 32)) | (data[(p >> 5) + 1] >>> (32 - ((p + bl) % 32)))) & mask;
        } else {
            re = (data[p >> 5] >>> (32 - (p + bl) % 32)) & mask;
        }
        p += bl;
        return re;
    }
 
    this.write = function(i) {
        var i, hi, li;
        i &= mask;
        if (32 - (l % 32) < bl) {
            data[l >> 5] |= i >>> (bl - (32 - (l % 32)));
            data[(l >> 5) + 1] |= (i << (32 - ((l + bl) % 32))) & _m;
        } else {
            data[l >> 5] |= (i << (32 - ((l + bl) % 32))) & _m;
        }
        l += bl;
    }
 
    this.eof = function() {
        return p >= l;
    }
 
    this.reset = function() {
        p = 0;
        mask = m(bl);
    }
    this.resetAll = function() {
        data = [];
        p = 0;
        l = 0;
        bl = 8;
        mask = m(bl);
        _m = 0xFFFFFFFF;
    }
 
    this.setBitLength = function(len) {
        bl = Math.max(len | 0, 1);
        mask = m(bl);
    }
 
    this.toHexString = function() {
        var re = [];
        for (var i = 0; i < data.length; i++) {
            if (data[i] < 0) {
                re.push(pad((data[i] >>> 16).toString(16), 4) + pad((data[i] & 0xFFFF).toString(16), 4));
            } else {
                re.push(pad(data[i].toString(16), 8));
            }
        }
        return re.join("");
    }
 
    this.toBinaryString = function() {
        var re = [];
        for (var i = 0; i < data.length; i++) {
            if (data[i] < 0) {
                re.push(pad((data[i] >>> 1).toString(2), 31) + (data[i] & 1));
            } else {
                re.push(pad(data[i].toString(2), 32));
            }
        }
        return re.join("").substring(0, l);
    }
 
    this.toCString = function() {
        var _p = p,
        _bl = bl,
        re = [];
        this.setBitLength(13);
        this.reset();
        while (p < l) re.push(C(this.read()));
        this.setBitLength(_bl);
        p = _p;
        return C(l >>> 13) + C(l & m(13)) + re.join("");
    }
 
    this.fromCString = function(str) {
        this.resetAll();
        this.setBitLength(13);
        for (var i = 2; i < str.length; i++) this.write(D(str, i));
        l = (D(str, 0) << 13) | (D(str, 1) & m(13));
        return this;
    }
 
    this.clone = function() {
        return new Binary(data, p, l, bl);
    }
    function m(len) {
        return (1 << len) - 1;
    }
    function pad(s, len) {
        return (new Array(len + 1)).join("0").substring(s.length) + s;
    }
    function C(i) {
        return String.fromCharCode(i + 0x4e00);
    }
    function D(s, i) {
        return s.charCodeAt(i) - 0x4e00;
    }
}
 
function lzw_compress(str) {
    var b = new Binary(),
    code_index = -1,
    char_len = 8;
    var str = str.replace(/[\u0100-\uFFFF]/g,
    function(s) {
        return "\&\#u" + pad(s.charCodeAt(0).toString(16), 4) + ";";
    });
    var dic = {},
    cp = [],
    cpi,
    bl = 8;
    b.setBitLength(bl);
    for (var i = 0; i < (1 << char_len) + 2; i++) dic[i] = ++code_index;
    cp[0] = str.charCodeAt(0);
    for (var i = 1; i < str.length; i++) {
        cp[1] = str.charCodeAt(i);
        cpi = (cp[0] << 16) | cp[1];
        if (dic[cpi] == undefined) {
            dic[cpi] = (++code_index);
            if (cp[0] > m(bl)) {
                b.write(0x80);
                b.setBitLength(++bl);
            }
            b.write(cp[0]);
            cp[0] = cp[1];
        } else {
            cp[0] = dic[cpi];
        }
    }
    b.write(cp[0]);
    function pad(s, len) {
        return (new Array(len + 1)).join("0").substring(s.length) + s;
    }
    function m(len) {
        return (1 << len) - 1;
    }
    return b.toCString();
}
 
function lzw_decompress(s) {
    var b = new
    function() {
        var d = [],
        p = 0,
        l = 0,
        L = 13,
        k = m(L),
        _m = 0xFFFFFFFF;
        this.r = function() {
            var r;
            if (32 - (p % 32) < L) r = (((d[p >> 5] & m(32 - (p % 32))) << ((p + L) % 32)) | (d[(p >> 5) + 1] >>> (32 - ((p + L) % 32)))) & k;
            else r = (d[p >> 5] >>> (32 - (p + L) % 32)) & k;
            p += L;
            return r;
        };
        this.w = function(i) {
            i &= k;
            if (32 - (l % 32) < L) {
                d[l >> 5] |= i >>> (L - (32 - (l % 32)));
                d[(l >> 5) + 1] |= (i << (32 - ((l + L) % 32))) & _m;
            } else d[l >> 5] |= (i << (32 - ((l + L) % 32))) & _m;
            l += L;
        };
        this.e = function() {
            return p >= l;
        };
        this.l = function(len) {
            L = Math.max(len | 0, 1);
            k = m(L);
        };
        function m(len) {
            return (1 << len) - 1;
        }
        function pad(s, l) {
            return (new Array(l + 1)).join("0").substring(s.length) + s;
        }
        for (var i = 2; i < s.length; i++) this.w(s.charCodeAt(i) - 0x4e00);
        l = ((s.charCodeAt(0) - 0x4e00) << 13) | ((s.charCodeAt(1) - 0x4e00) & m(13));
        p = 0;
    };
    var R = [],
    C = -1,
    D = {},
    P = [],
    L = 8;
    for (i = 0; i < (1 << L) + 2; i++) D[i] = String.fromCharCode(++C);
    b.l(L);
    P[0] = b.r();
    while (!b.e()) {
        P[1] = b.r();
        if (P[1] == 0x80) {
            b.l(++L);
            P[1] = b.r();
        }
        if (D[P[1]] == undefined) D[++C] = D[P[0]] + D[P[0]].charAt(0);
        else D[++C] = D[P[0]] + D[P[1]].charAt(0);
        R.push(D[P[0]]);
        P[0] = P[1];
    }
    R.push(D[P[0]]);
    return R.join("").replace(/\&\#u[0-9a-fA-F]{4};/g,
    function(w) {
        return String.fromCharCode(parseInt(w.substring(3, 7), 16));
    });
}
